#pragma once
namespace graph_basic
{
namespace details
{
   struct graph_mutable_t;
   struct graph_inf_t
   {
      graph_inf_t(graph_id_t id, lat_lon_t min_lat, lat_lon_t min_lon,
                  lat_lon_t max_lat, lat_lon_t max_lon, graph_int_t vertex_count,
                  graph_int_t edge_count)
         :  id_(id)
         ,  min_lat_(min_lat)
         ,  min_lon_(min_lon)
         ,  max_lat_(max_lat)
         ,  max_lon_(max_lon)
         ,  vertex_count_(vertex_count)
         ,  edge_count_(edge_count)
      {}
      graph_inf_t()
      {}
      graph_id_t id_;
      lat_lon_t min_lat_;
      lat_lon_t min_lon_;
      lat_lon_t max_lat_;
      lat_lon_t max_lon_;
      graph_int_t vertex_count_;
      graph_int_t edge_count_;
   };
}

struct graph_base_t
{
   graph_base_t(graph_id_t graph_id, 
                size_t vertex_count, size_t edge_count, 
                lat_lon_t min_lat, lat_lon_t min_lon, 
                lat_lon_t max_lat, lat_lon_t max_lon)
      :  inf_(graph_id, min_lat, min_lon, max_lat, max_lon, vertex_count, edge_count)
   {}
   graph_base_t(details::graph_inf_t inf)
      :  inf_(inf)
   {}
   graph_id_t get_graph_id() const;
   size_t get_edge_count() const;
   size_t get_vertex_count() const;
   lat_lon_t get_min_lat() const;
   lat_lon_t get_min_lon() const;
   lat_lon_t get_max_lat() const;
   lat_lon_t get_max_lon() const;
protected:
   details::graph_inf_t inf_;
};

struct graph_sequential_t: public graph_base_t
{
   graph_sequential_t(details::graph_inf_t inf)
      :  graph_base_t(inf) 
   {}
   
   vertex_t const & operator [] (size_t idx) const;   
   vertex_t& operator [] (size_t idx);   
   //TODO: make private
   std::pair<details::edge_real_t*, details::edge_real_t*> get_vertex_edges(vertex_in_graph_id_t idx) const;
private:
   char data_[];
   friend struct details::graph_mutable_t;
};

#pragma pack(1)
struct graph_not_sequential_t: public graph_base_t
{
   //assuming that vertices and edges allocated with malloc
   graph_not_sequential_t(details::graph_inf_t inf,
                          vertex_t* vertices, details::edge_real_t* edges)
      :  graph_base_t(inf) 
      ,  vertices_(vertices)
      ,  edges_(edges)
   {}
   
   vertex_t const & operator [] (size_t idx) const;
   vertex_t& operator [] (size_t idx);
   std::pair<details::edge_real_t*, details::edge_real_t*> get_vertex_edges(vertex_in_graph_id_t idx) const;
   ~graph_not_sequential_t()
   {
      free(vertices_);
      free(edges_);
   }
private:
   vertex_t* vertices_;
   details::edge_real_t* edges_;
   friend struct details::graph_mutable_t;
};


typedef boost::shared_ptr<graph_not_sequential_t> graph_not_sequential_ptr_t;
typedef boost::shared_ptr<graph_sequential_t> graph_sequential_ptr_t;
typedef graph_not_sequential_t graph_t;
typedef boost::shared_ptr<graph_t> graph_ptr_t;

inline vertex_t& source(edge_t e, graph_t& g);
inline vertex_t const & source(edge_t const e, graph_t const & g);
inline vertex_t& target(edge_t e, graph_t& g);
inline vertex_t const & target(edge_t const e, graph_t const & g);
template<class G>
vertex_id_pair_t nearest_vertices(G& graph, point_t first, point_t second);

template<class G>
struct graph_traits
{
   struct some_iterator_t;
   typedef some_iterator_t vertex_iterator;
   typedef some_iterator_t edge_iterator;
   typedef some_iterator_t adj_vertices_iterator;
   typedef some_iterator_t adj_edges_iterator;
   struct ERROR_NOT_SUPPORTED_GRAPH_TYPE;
   ERROR_NOT_SUPPORTED_GRAPH_TYPE err;
};

namespace details
{
   struct graph_mutable_t
   {
      graph_inf_t inf_;
      graph_mutable_t(graph_inf_t inf);   
      void make_sequential(graph_sequential_ptr_t& result, size_t& size);
      void make_not_sequential(graph_not_sequential_ptr_t& result,
                               vertex_t** vertices, edge_real_t** edges);
      std::vector<vertex_mutable_t>& get_adj_list();
      vertex_mutable_t& add_vertex(point_t p);
   private:
      std::vector<vertex_mutable_t> adj_list_;
   };
   typedef boost::shared_ptr<graph_mutable_t> graph_mutable_ptr_t;
}
}